namespace Angelus.Data.Linq.OracleClient
{
	using System;
    using System.Expressions;
    using System.Collections.Generic;
    using System.Data.DLinq;
    using System.Data.DLinq.ProviderBase;
    using System.Reflection;
    using System.Text;

    internal class OracleMethodCallConverter : MethodCallConverter
    {
        #region Constants

        #endregion

        #region Constructors

        /// <summary>
        /// 
        /// </summary>
        /// <param name="factory"></param>
        /// <param name="eval"></param>
        internal OracleMethodCallConverter(SqlFactory factory, SqlEval eval)
            : base(factory, eval)
        {
        }

        #endregion

        #region Visitor Implementation

        /// <summary>
        /// 
        /// </summary>
        /// <param name="m"></param>
        /// <returns></returns>
        internal override SqlNode VisitMember(SqlMember m)
        {
            SqlExpression expression = VisitExpression(m.Expression);
            MemberInfo member = m.Member;
            Expression sourceExpression = m.SourceExpression;

            if ((typeof (string) != expression.ClrType) || ("Length" != member.Name))
            {
                if (TypeSystem.IsNullableType(expression.ClrType))
                {
                    if ("Value" != member.Name)
                    {
                        if ("HasValue" != member.Name)
                        {
                            throw MemberNotSupported(member);
                        }
                        return base.sql.Unary(SqlNodeType.IsNotNull, expression, sourceExpression);
                    }
                    return base.sql.UnaryValueOf(expression, sourceExpression);
                }
                if (typeof (DateTime) == expression.ClrType)
                {
                    string part = GetDatePart(member.Name);
                    if (null == part)
                    {
                        if ("TimeOfDay" == member.Name)
                        {
                            return base.sql.ConvertTo(typeof (TimeSpan), base.sql.Add(new SqlExpression[]
                                                                                          {
                                                                                              base.sql.Multiply(
                                                                                                  base.sql.
                                                                                                      ConvertToBigint(
                                                                                                      base.sql.
                                                                                                          FunctionCall(
                                                                                                          typeof (int),
                                                                                                          "TO_CHAR",
                                                                                                          new SqlExpression
                                                                                                              []
                                                                                                              {
                                                                                                                  expression
                                                                                                                  ,
                                                                                                                  new SqlVariable
                                                                                                                      (typeof
                                                                                                                           (
                                                                                                                           void
                                                                                                                           ),
                                                                                                                       null,
                                                                                                                       "'HH24'",
                                                                                                                       sourceExpression)
                                                                                                              },
                                                                                                          sourceExpression)),
                                                                                                  0x861c46800),
                                                                                              base.sql.Multiply(
                                                                                                  base.sql.
                                                                                                      ConvertToBigint(
                                                                                                      base.sql.
                                                                                                          FunctionCall(
                                                                                                          typeof (int),
                                                                                                          "TO_CHAR",
                                                                                                          new SqlExpression
                                                                                                              []
                                                                                                              {
                                                                                                                  expression
                                                                                                                  ,
                                                                                                                  new SqlVariable
                                                                                                                      (typeof
                                                                                                                           (
                                                                                                                           void
                                                                                                                           ),
                                                                                                                       null,
                                                                                                                       "'MI'",
                                                                                                                       sourceExpression)
                                                                                                              },
                                                                                                          sourceExpression)),
                                                                                                  0x23c34600),
                                                                                              base.sql.Multiply(
                                                                                                  base.sql.
                                                                                                      ConvertToBigint(
                                                                                                      base.sql.
                                                                                                          FunctionCall(
                                                                                                          typeof (int),
                                                                                                          "TO_CHAR",
                                                                                                          new SqlExpression
                                                                                                              []
                                                                                                              {
                                                                                                                  expression
                                                                                                                  ,
                                                                                                                  new SqlVariable
                                                                                                                      (typeof
                                                                                                                           (
                                                                                                                           void
                                                                                                                           ),
                                                                                                                       null,
                                                                                                                       "'SS'",
                                                                                                                       sourceExpression)
                                                                                                              },
                                                                                                          sourceExpression)),
                                                                                                  0x989680),
                                                                                              base.sql.Multiply(
                                                                                                  base.sql.
                                                                                                      ConvertToBigint(
                                                                                                      base.sql.
                                                                                                          FunctionCall(
                                                                                                          typeof (int),
                                                                                                          "TO_CHAR",
                                                                                                          new SqlExpression
                                                                                                              []
                                                                                                              {
                                                                                                                  expression
                                                                                                                  ,
                                                                                                                  new SqlVariable
                                                                                                                      (typeof
                                                                                                                           (
                                                                                                                           void
                                                                                                                           ),
                                                                                                                       null,
                                                                                                                       "'FF'",
                                                                                                                       sourceExpression)
                                                                                                              },
                                                                                                          sourceExpression)),
                                                                                                  0x2710)
                                                                                          }));
                        }
                        if ("DayOfWeek" == member.Name)
                        {
                            SqlExpression partExpression = base.sql.FunctionCall(typeof (int), "TO_CHAR",
                                                                                 new SqlExpression[]
                                                                                     {
                                                                                         expression,
                                                                                         new SqlVariable(typeof (void),
                                                                                                         null, "'D'",
                                                                                                         sourceExpression)
                                                                                     },
                                                                                 sourceExpression);
                            return
                                base.sql.ConvertToInt(
                                    base.sql.Mod(
                                        base.sql.Add(new SqlExpression[]
                                                         {
                                                             partExpression,
                                                             base.sql.Add(
                                                                 new SqlVariable(typeof (int),
                                                                                 base.sql.Default(typeof (int)),
                                                                                 "@@DATEFIRST", sourceExpression), 6)
                                                         }), 7));
                        }
                        throw MemberNotSupported(member);
                    }
                    return base.sql.FunctionCall(typeof (int), "TO_CHAR",
                                                 new SqlExpression[]
                                                     {
                                                         expression,
                                                         new SqlVariable(typeof (void), null, part, sourceExpression)
                                                     },
                                                 sourceExpression);
                }
                if (typeof (TimeSpan) == expression.ClrType)
                {
                    expression.SetClrType(typeof (long));
                    string name = member.Name;
                    if (null != name)
                    {
                        switch (name)
                        {
                            case "Ticks":
                                return expression;
                            case "TotalMilliseconds":
                                return base.sql.Divide(base.sql.ConvertToDouble(expression), 0x2710);
                            case "TotalSeconds":
                                return base.sql.Divide(base.sql.ConvertToDouble(expression), 0x989680);
                            case "TotalMinutes":
                                return base.sql.Divide(base.sql.ConvertToDouble(expression), 0x23c34600);
                            case "TotalHours":
                                return base.sql.Divide(base.sql.ConvertToDouble(expression), 0x861c46800);
                            case "TotalDays":
                                return base.sql.Divide(base.sql.ConvertToDouble(expression), 0xc92a69c000);
                            case "Milliseconds":
                                return
                                    base.sql.ConvertToInt(
                                        base.sql.Mod(base.sql.ConvertToBigint(base.sql.Divide(expression, 0x2710)),
                                                     0x3e8));
                            case "Seconds":
                                return
                                    base.sql.ConvertToInt(
                                        base.sql.Mod(base.sql.ConvertToBigint(base.sql.Divide(expression, 0x989680)), 60));
                            case "Minutes":
                                return
                                    base.sql.ConvertToInt(
                                        base.sql.Mod(base.sql.ConvertToBigint(base.sql.Divide(expression, 0x23c34600)),
                                                     60));
                            case "Hours":
                                return
                                    base.sql.ConvertToInt(
                                        base.sql.Mod(
                                            base.sql.ConvertToBigint(base.sql.Divide(expression, 0x861c46800)), 0x18));
                            case "Days":
                                return base.sql.ConvertToInt(base.sql.Divide(expression, 0xc92a69c000));
                        }
                    }
                    throw MemberNotSupported(member);
                }
                return base.sql.Member(expression, member);
            }
            return base.sql.FunctionCall(typeof (int), "LENGTH", new SqlExpression[] {expression}, sourceExpression);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="methodCall"></param>
        /// <returns></returns>
        internal override SqlExpression VisitMethodCall(SqlMethodCall methodCall)
        {
            methodCall.Object = VisitExpression(methodCall.Object);

            for (int index = 0, max = methodCall.Arguments.Count; index < max; index++)
            {
                methodCall.Arguments[index] = VisitExpression(methodCall.Arguments[index]);
            }

            Type declaringType = methodCall.Method.DeclaringType;
            Expression sourceExpression = methodCall.SourceExpression;
            SqlExpression expression = null;

            if (methodCall.Method.IsStatic)
            {
                if (typeof (Math) != declaringType)
                {
                    if (typeof (string) == declaringType)
                    {
                        expression = this.TranslateStringStaticMethod(methodCall, sourceExpression);
                    }
                    else if (typeof (DateTime) == declaringType)
                    {
                        // expression = this.TranslateDateTimeStaticMethod(methodCall, sourceExpression);
                    }
                    else if (typeof (TimeSpan) == declaringType)
                    {
                        // expression = this.TranslateTimeSpanStaticMethod(methodCall, sourceExpression);
                    }
                    else if (typeof (Convert) == declaringType)
                    {
                        return this.TranslateDecimalMethod(methodCall, sourceExpression);
                    }

                    if (null == expression)
                    {
                        string name = null;
                        ProviderType provider = base.sql.Default(methodCall.Method.ReturnType);
                        if (!provider.IsRuntimeOnlyType && ((name = methodCall.Method.Name) != null))
                        {
                            switch (name)
                            {
                                case "op_Equality":
                                    return base.sql.Binary(SqlNodeType.EQ, methodCall.Arguments[0],
                                                           methodCall.Arguments[1]);

                                case "op_Inequality":
                                    return base.sql.Binary(SqlNodeType.NE, methodCall.Arguments[0],
                                                           methodCall.Arguments[1]);

                                case "op_LessThan":
                                    return base.sql.Binary(SqlNodeType.LT, methodCall.Arguments[0],
                                                           methodCall.Arguments[1]);

                                case "op_LessThanOrEqual":
                                    return base.sql.Binary(SqlNodeType.LE, methodCall.Arguments[0],
                                                           methodCall.Arguments[1]);

                                case "op_GreaterThan":
                                    return base.sql.Binary(SqlNodeType.GT, methodCall.Arguments[0],
                                                           methodCall.Arguments[1]);

                                case "op_GreaterThanOrEqual":
                                    return base.sql.Binary(SqlNodeType.GE, methodCall.Arguments[0],
                                                           methodCall.Arguments[1]);

                                case "op_Explicit":
                                case "op_Implicit":
                                    return base.sql.ConvertTo(methodCall.ClrType, methodCall.Arguments[0]);

                                case "op_Multiply":
                                    return base.sql.Binary(SqlNodeType.Mul, methodCall.Arguments[0],
                                                           methodCall.Arguments[1]);

                                case "op_Division":
                                    return base.sql.Binary(SqlNodeType.Div, methodCall.Arguments[0],
                                                           methodCall.Arguments[1]);

                                case "op_Substraction":
                                    return base.sql.Binary(SqlNodeType.Sub, methodCall.Arguments[0],
                                                           methodCall.Arguments[1]);

                                case "op_Addition":
                                    return base.sql.Binary(SqlNodeType.Add, methodCall.Arguments[0],
                                                           methodCall.Arguments[1]);

                                case "op_Modulus":
                                    return base.sql.Binary(SqlNodeType.Mod, methodCall.Arguments[0],
                                                           methodCall.Arguments[1]);

                                case "op_BitwiseAnd":
                                    return base.sql.Binary(SqlNodeType.BitAnd, methodCall.Arguments[0],
                                                           methodCall.Arguments[1]);

                                case "op_BitwiseOr":
                                    return base.sql.Binary(SqlNodeType.BitOr, methodCall.Arguments[0],
                                                           methodCall.Arguments[1]);

                                case "op_ExclusiveOr":
                                    return base.sql.Binary(SqlNodeType.BitXor, methodCall.Arguments[0],
                                                           methodCall.Arguments[1]);

                                case "op_UnaryNegation":
                                    return base.sql.Unary(SqlNodeType.Negate, methodCall.Arguments[0], sourceExpression);

                                case "op_OnesComplement":
                                    return base.sql.Unary(SqlNodeType.BitNot, methodCall.Arguments[0], sourceExpression);

                                case "op_False":
                                    return base.sql.Unary(SqlNodeType.Not, methodCall.Arguments[0], sourceExpression);

                                case "op_True":
                                    return methodCall.Arguments[0];
                            }
                        }
                        if ("op_Implicit" != methodCall.Method.Name)
                        {
                            throw new Exception("Static method " + methodCall.Method +
                                                " has not supported translation to ORACLE.");
                        }
                        return methodCall;
                    }
                    return expression;
                }
                return this.TranslateMathMethod(methodCall.Method, methodCall.Arguments, sourceExpression);
            }
            if (("Equals" == methodCall.Method.Name) && (1 == methodCall.Arguments.Count))
            {
                return base.sql.Binary(SqlNodeType.EQ, methodCall.Object, methodCall.Arguments[0]);
            }
            if (("ToString" == methodCall.Method.Name) && (0 == methodCall.Arguments.Count))
            {
                SqlExpression convert = methodCall.Object;
                if (!convert.ClrType.IsPrimitive && (typeof (DateTime) != convert.ClrType))
                {
                    throw new Exception("Method ToString can only be translated to ORACLE for pimitive types");
                }
                return this.ConvertToString(convert);
            }
            if (typeof (string) == declaringType)
            {
                return this.TranslateStringMethod(methodCall.Method, methodCall.Object, methodCall.Arguments,
                                                  sourceExpression);
            }
            if (typeof (TimeSpan) == declaringType)
            {
                // expression = this.TranslateTimeSpanInstanceMethod(methodCall, sourceExpression);
            }
            else if (typeof (DateTime) == declaringType)
            {
                // expression = this.TranslateDateTimeInstanceMethod(methodCall, sourceExpression);
            }
            if (null != expression)
            {
                return expression;
            }
            return methodCall;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sox"></param>
        /// <returns></returns>
        internal override SqlExpression VisitNew(SqlNew sox)
        {
            SqlExpression expression = base.VisitNew(sox);
            Expression sourceExpression = sox.SourceExpression;
            if (!IsServerType(sox.ClrType))
            {
                return expression;
            }
            if (typeof (string) == sox.ClrType)
            {
                if (((typeof (string) != sox.ClrType) || (2 != sox.Args.Count)) ||
                    ((typeof (char) != sox.Args[0].ClrType) || (typeof (int) != sox.Args[0].ClrType)))
                {
                    throw new Exception("The used string constructor is not supported");
                }
                return base.sql.FunctionCall(typeof (string), "REPLICATE",
                                             new SqlExpression[] {sox.Args[0], sox.Args[1]}, sourceExpression);
            }
            if (typeof (TimeSpan) != sox.ClrType)
            {
                if (typeof (DateTime) != sox.ClrType)
                {
                    throw new Exception("Constructor of server type " + sox.ClrType + " not implemented");
                }
                //return this.VisitNe
            }
            return sox;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="typeCase"></param>
        /// <returns></returns>
        internal override SqlExpression VisitTypeCase(SqlTypeCase typeCase)
        {
            typeCase.Discriminator = base.VisitExpression(typeCase.Discriminator);

            List<SqlExpression> matches = new List<SqlExpression>();
            List<SqlExpression> values = new List<SqlExpression>();

            bool isNew = true;
            foreach (SqlTypeCaseWhen when in typeCase.Whens)
            {
                SqlExpression match = VisitExpression(when.Match);
                SqlExpression value = VisitExpression(when.TypeBinding);
                isNew = isNew && (value is SqlNew);
                matches.Add(match);
                values.Add(value);
            }
            if (!isNew)
            {
                return base.sql.Case(typeCase.ClrType, typeCase.Discriminator, matches, values,
                                     typeCase.SourceExpression);
            }

            for (int index = 0, max = typeCase.Whens.Count; index < max; index++)
            {
                SqlTypeCaseWhen when = typeCase.Whens[index];
                when.Match = matches[index];
                when.TypeBinding = values[index];
            }
            return typeCase;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="unary"></param>
        /// <returns></returns>
        internal override SqlExpression VisitUnaryOperator(SqlUnary unary)
        {
            if (SqlNodeType.Convert == unary.NodeType)
            {
                Type clrType = unary.ClrType;
                SqlExpression operand = unary.Operand;
                if ((typeof (char) == clrType) || (typeof (char) == operand.ClrType))
                {
                    operand = VisitExpression(unary.Operand);
                    unary.Operand = operand;
                    return base.CastExpression(clrType, operand);
                }
            }
            return base.VisitUnaryOperator(unary);
        }

        #endregion

        #region Private Implementation

        /// <summary>
        /// 
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        private SqlExpression ConvertDoubleToString(SqlExpression expression)
        {
            SqlExpression convertExpression = base.sql.FunctionCall(typeof (void), "NVARCHAR",
                                                                    new SqlExpression[]
                                                                        {
                                                                            base.sql.Literal(30,
                                                                                             expression.SourceExpression)
                                                                        }, expression.SourceExpression);
            return base.sql.FunctionCall(typeof (string), "TO_CHAR",
                                         new SqlExpression[]
                                             {
                                                 convertExpression, expression,
                                                 base.sql.Literal(2, expression.SourceExpression)
                                             },
                                         expression.SourceExpression);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        private SqlExpression ConvertToString(SqlExpression expression)
        {
            if (typeof (string) == expression.ClrType)
            {
                return expression;
            }
            if (typeof (double) == expression.ClrType)
            {
                return this.ConvertDoubleToString(expression);
            }
            return base.sql.ConvertTo(typeof (string), expression);
        }

        ///// <summary>
        ///// 
        ///// </summary>
        ///// <param name="sqlDate"></param>
        ///// <param name="ms"></param>
        ///// <param name="source"></param>
        ///// <returns></returns>
        //private SqlExpression CreateDateTimeFromDateAndMs(SqlExpression sqlDate, SqlExpression ms, Expression source)
        //{
        //    SqlExpression convertExpression = base.sql.ConvertToBigint(ms);
        //    SqlExpression addExpression = base.sql.FunctionCall(typeof(DateTime), "
        //}

        //private SqlExpression CreateDateTimeFromDateAndTicks(SqlExpression sqlDate, SqlExpression sqlTicks, Expression source);

        /// <summary>
        /// 
        /// </summary>
        /// <param name="type"></param>
        /// <param name="functionName"></param>
        /// <param name="parameters"></param>
        /// <param name="sourceExpression"></param>
        /// <returns></returns>
        private SqlExpression CreateFunctionCallStatic1(Type type, string functionName, IList<SqlExpression> parameters,
                                                        Expression sourceExpression)
        {
            return base.sql.FunctionCall(type, functionName, new SqlExpression[] {parameters[0]}, sourceExpression);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="type"></param>
        /// <param name="functionName"></param>
        /// <param name="parameters"></param>
        /// <param name="sourceExpression"></param>
        /// <returns></returns>
        private SqlExpression CreateFunctionCallStatic2(Type type, string functionName, IList<SqlExpression> parameters,
                                                        Expression sourceExpression)
        {
            return base.sql.FunctionCall(type, functionName, new SqlExpression[] {parameters[0], parameters[1]},
                                         sourceExpression);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <param name="escape"></param>
        /// <returns></returns>
        private static string EscapeLikeText(string value, char escape)
        {
            StringBuilder sb = new StringBuilder(value.Length);
            foreach (char c in value)
            {
                if ((('%' == c) || ('_' == c)) || ((('[' == c) || ('^' == c)) || (escape == c)))
                {
                    sb.Append(escape);
                }
                sb.Append(c);
            }
            return sb.ToString();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="memberName"></param>
        /// <returns></returns>
        private static string GetDatePart(string memberName)
        {
            switch (memberName)
            {
                case "Year":
                    return "'YYYY'";

                case "Month":
                    return "'MM'";

                case "Day":
                    return "'DD'";

                case "DayOfYear":
                    return "'D'";

                case "Hour":
                    return "'HH24'";

                case "Minute":
                    return "'MI'";

                case "Second":
                    return "'SS'";

                case "Millisecond":
                    return "'FF'";
            }
            return null;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        private static bool IsServerType(Type type)
        {
            if ((typeof (string) != type) && (typeof (DateTime) != type))
            {
                return (typeof (TimeSpan) == type);
            }
            return true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="member"></param>
        /// <returns></returns>
        private static Exception MemberNotSupported(MemberInfo member)
        {
            return
                new Exception(
                    string.Concat(new object[]
                                      {
                                          "The member ", member.DeclaringType, ".", member.Name,
                                          " has not supported translation to ORACLE"
                                      }));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        private static bool RequiresEscape(string value)
        {
            if ((!value.Contains("%") && !value.Contains("_")) && !value.Contains("["))
            {
                return value.Contains("^");
            }
            return true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="methodCall"></param>
        /// <param name="sourceExpression"></param>
        /// <returns></returns>
        private SqlExpression TranslateConvertStaticMethod(SqlMethodCall methodCall, Expression sourceExpression)
        {
            if (1 != methodCall.Arguments.Count)
            {
                return null;
            }
            SqlExpression argument = methodCall.Arguments[0];
            string name = methodCall.Method.Name;
            Type type = null;
            if (!string.IsNullOrEmpty(name))
            {
                switch (name)
                {
                    case "ToBoolean":
                        type = typeof (bool);
                        break;

                    case "ToDecimal":
                        type = typeof (decimal);
                        break;

                    case "ToByte":
                        type = typeof (byte);
                        break;

                    case "ToChar":
                        type = typeof (char);
                        break;

                    case "ToDateTime":
                        type = typeof (DateTime);
                        break;

                    case "ToDouble":
                        type = typeof (double);
                        break;

                    case "ToInt16":
                        type = typeof (short);
                        break;

                    case "ToInt32":
                        type = typeof (int);
                        break;

                    case "ToInt64":
                        type = typeof (long);
                        break;

                    case "ToSingle":
                        type = typeof (float);
                        break;

                    case "ToString":
                        type = typeof (string);
                        break;

                    case "ToSByte":
                        type = typeof (sbyte);
                        break;

                    case "ToUInt16":
                        type = typeof (short);
                        break;

                    case "ToUInt32":
                        type = typeof (int);
                        break;

                    case "ToUInt64":
                        type = typeof (long);
                        break;
                }
            }
            if (null == type)
            {
                return null;
            }
            if ((typeof (string) == type) && (typeof (double) == argument.ClrType))
            {
                return this.ConvertDoubleToString(argument);
            }
            return base.CastExpression(type, argument);
        }

        //private SqlExpression TranslateDateTimeInstanceMethod(SqlMethodCall mc, Expression source);
        //private SqlExpression TranslateDateTimeStaticMethod(SqlMethodCall mc, Expression source);

        /// <summary>
        /// 
        /// </summary>
        /// <param name="methodCall"></param>
        /// <param name="source"></param>
        /// <returns></returns>
        internal SqlExpression TranslateDecimalMethod(SqlMethodCall methodCall, Expression sourceExpression)
        {
            string name = null;
            if ((2 == methodCall.Arguments.Count) && (null != (name = methodCall.Method.Name)))
            {
                if ("Multiply" == name)
                {
                    return base.sql.Binary(SqlNodeType.Mul, methodCall.Arguments[0], methodCall.Arguments[1]);
                }
                if ("Divide" == name)
                {
                    return base.sql.Binary(SqlNodeType.Div, methodCall.Arguments[0], methodCall.Arguments[1]);
                }
                if ("Substract" == name)
                {
                    return base.sql.Binary(SqlNodeType.Sub, methodCall.Arguments[0], methodCall.Arguments[1]);
                }
                if ("Add" == name)
                {
                    return base.sql.Binary(SqlNodeType.Add, methodCall.Arguments[0], methodCall.Arguments[1]);
                }
                if ("Remainder" == name)
                {
                    return base.sql.Binary(SqlNodeType.Mod, methodCall.Arguments[0], methodCall.Arguments[1]);
                }
                if ("Negate" == name)
                {
                    return base.sql.Unary(SqlNodeType.Negate, methodCall.Arguments[0], sourceExpression);
                }
            }
            throw new Exception("The decimal method " + methodCall.Method.Name +
                                " has no supported translation to ORACLE");
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="method"></param>
        /// <param name="parameters"></param>
        /// <param name="sourceExpression"></param>
        /// <returns></returns>
        internal SqlExpression TranslateMathMethod(MethodInfo method, List<SqlExpression> parameters,
                                                   Expression sourceExpression)
        {
            string name = method.Name;
            if (!string.IsNullOrEmpty(name))
            {
                switch (name)
                {
                    case "Abs":
                        if (1 == parameters.Count)
                        {
                            return base.sql.FunctionCall(parameters[0].ClrType, "ABS",
                                                         new SqlExpression[] {parameters[0]}, sourceExpression);
                        }
                        throw new Exception("Unhandled overload of method System.Math." + method.Name);

                    case "Acos":
                        if (1 == parameters.Count)
                        {
                            return this.CreateFunctionCallStatic1(typeof (double), "ACOS", parameters, sourceExpression);
                        }
                        throw new Exception("Unhandled overload of method System.Math." + method.Name);

                    case "Asin":
                        if (1 == parameters.Count)
                        {
                            return this.CreateFunctionCallStatic1(typeof (double), "ASIN", parameters, sourceExpression);
                        }
                        throw new Exception("Unhandled overload of method System.Math." + method.Name);

                    case "Atan":
                        if (1 == parameters.Count)
                        {
                            return this.CreateFunctionCallStatic1(typeof (double), "ATAN", parameters, sourceExpression);
                        }
                        throw new Exception("Unhandled overload of method System.Math." + method.Name);

                    case "Atan2":
                        if (2 == parameters.Count)
                        {
                            return this.CreateFunctionCallStatic2(typeof (double), "ATAN2", parameters, sourceExpression);
                        }
                        throw new Exception("Unhandled overload of method System.Math." + method.Name);

                    case "BigMul":
                        if (2 == parameters.Count)
                        {
                            return
                                base.sql.Multiply(new SqlExpression[]
                                                      {
                                                          base.sql.ConvertToBigint(parameters[0]),
                                                          base.sql.ConvertToBigint(parameters[1])
                                                      });
                        }
                        throw new Exception("Unhandled overload of method System.Math." + method.Name);

                    case "Ceiling":
                        if (1 == parameters.Count)
                        {
                            return this.CreateFunctionCallStatic1(typeof (double), "CEIL", parameters, sourceExpression);
                        }
                        throw new Exception("Unhandled overload of method System.Math." + method.Name);

                    case "Cos":
                        if (1 == parameters.Count)
                        {
                            return this.CreateFunctionCallStatic1(typeof (double), "COS", parameters, sourceExpression);
                        }
                        throw new Exception("Unhandled overload of method System.Math." + method.Name);

                    case "Cosh":
                        if (1 == parameters.Count)
                        {
                            return this.CreateFunctionCallStatic1(typeof (double), "COSH", parameters, sourceExpression);
                        }
                        throw new Exception("Unhandled overload of method System.Math." + method.Name);

                    case "Exp":
                        if (1 == parameters.Count)
                        {
                            return this.CreateFunctionCallStatic1(typeof (double), "EXP", parameters, sourceExpression);
                        }
                        throw new Exception("Unhandled overload of method System.Math." + method.Name);

                    case "Floor":
                        if (1 == parameters.Count)
                        {
                            return this.CreateFunctionCallStatic1(typeof (double), "FLOOR", parameters, sourceExpression);
                        }
                        throw new Exception("Unhandled overload of method System.Math." + method.Name);

                    case "Log":
                        if (1 == parameters.Count)
                        {
                            return this.CreateFunctionCallStatic1(typeof (double), "LN", parameters, sourceExpression);
                        }
                        throw new Exception("Unhandled overload of method System.Math." + method.Name);

                    case "Log10":
                        if (1 == parameters.Count)
                        {
                            return this.CreateFunctionCallStatic1(typeof (double), "LOG", parameters, sourceExpression);
                        }
                        throw new Exception("Unhandled overload of method System.Math." + method.Name);

                    case "Max":
                        if (2 == parameters.Count)
                        {
                            SqlExpression expression = base.sql.Binary(SqlNodeType.LT, parameters[0], parameters[1]);
                            return base.sql.SearchedCase(new SqlWhen[] {new SqlWhen(expression, parameters[1])},
                                                         parameters[0], sourceExpression);
                        }
                        throw new Exception("Unhandled overload of method System.Math." + method.Name);

                    case "Min":
                        if (2 == parameters.Count)
                        {
                            SqlExpression expression = base.sql.Binary(SqlNodeType.LT, parameters[0], parameters[1]);
                            return base.sql.SearchedCase(new SqlWhen[] {new SqlWhen(expression, parameters[0])},
                                                         parameters[1], sourceExpression);
                        }
                        throw new Exception("Unhandled overload of method System.Math." + method.Name);

                    case "Pow":
                        if (2 == parameters.Count)
                        {
                            return this.CreateFunctionCallStatic2(typeof (double), "POWER", parameters, sourceExpression);
                        }
                        throw new Exception("Unhandled overload of method System.Math." + method.Name);

                    case "Round":
                        if (1 == parameters.Count)
                        {
                            return this.CreateFunctionCallStatic1(typeof (double), "ROUND", parameters, sourceExpression);
                        }
                        if (2 == parameters.Count)
                        {
                            return this.CreateFunctionCallStatic2(typeof (double), "ROUND", parameters, sourceExpression);
                        }
                        throw new Exception("Unhandled overload of method System.Math." + method.Name);

                    case "Sign":
                        if (1 == parameters.Count)
                        {
                            return base.sql.FunctionCall(parameters[0].ClrType, "SIGN",
                                                         new SqlExpression[] {parameters[0]}, sourceExpression);
                        }
                        throw new Exception("Unhandled overload of method System.Math." + method.Name);

                    case "Sin":
                        if (1 == parameters.Count)
                        {
                            return this.CreateFunctionCallStatic1(typeof (double), "SIN", parameters, sourceExpression);
                        }
                        throw new Exception("Unhandled overload of method System.Math." + method.Name);

                    case "Sinh":
                        if (1 == parameters.Count)
                        {
                            return this.CreateFunctionCallStatic1(typeof (double), "SINH", parameters, sourceExpression);
                        }
                        throw new Exception("Unhandled overload of method System.Math." + method.Name);

                    case "Sqrt":
                        if (1 == parameters.Count)
                        {
                            return this.CreateFunctionCallStatic1(typeof (double), "SQRT", parameters, sourceExpression);
                        }
                        throw new Exception("Unhandled overload of method System.Math." + method.Name);

                    case "Tan":
                        if (1 == parameters.Count)
                        {
                            return this.CreateFunctionCallStatic1(typeof (double), "TAN", parameters, sourceExpression);
                        }
                        throw new Exception("Unhandled overload of method System.Math." + method.Name);

                    case "Tanh":
                        if (1 == parameters.Count)
                        {
                            return this.CreateFunctionCallStatic1(typeof (double), "TANH", parameters, sourceExpression);
                        }
                        throw new Exception("Unhandled overload of method System.Math." + method.Name);

                    case "Truncate":
                        if (1 == parameters.Count)
                        {
                            return this.CreateFunctionCallStatic1(typeof (double), "TRUNC", parameters, sourceExpression);
                        }
                        if (2 == parameters.Count)
                        {
                            return this.CreateFunctionCallStatic2(typeof (double), "TRUNC", parameters, sourceExpression);
                        }
                        throw new Exception("Unhandled overload of method System.Math." + method.Name);
                }
            }
            throw new Exception("Unhandled static System.Math method " + method.Name);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="method"></param>
        /// <param name="expression"></param>
        /// <param name="parameters"></param>
        /// <param name="sourceExpression"></param>
        /// <returns></returns>
        internal SqlExpression TranslateStringMethod(MethodInfo method, SqlExpression expression,
                                                     List<SqlExpression> parameters, Expression sourceExpression)
        {
            string name = method.Name;
            if (!string.IsNullOrEmpty(name))
            {
                switch (name)
                {
                    case "Contains":
                        if (1 == parameters.Count)
                        {
                            string parameter = base.Eval(parameters[0]).ToString();
                            SqlExpression escape = null;
                            if (RequiresEscape(parameter))
                            {
                                parameter = EscapeLikeText(parameter, '~');
                                escape = base.sql.Literal("~", sourceExpression);
                            }
                            SqlExpression pattern = base.sql.Parameter("%" + parameter + "%", sourceExpression);
                            return base.sql.Like(expression, pattern, escape, sourceExpression);
                        }
                        throw new Exception("The used overload of System.String." + method.Name +
                                            " has no supported translation to ORACLE");

                    case "StartsWith":
                        if (1 == parameters.Count)
                        {
                            string parameter = base.Eval(parameters[0]).ToString();
                            SqlExpression escape = null;
                            if (RequiresEscape(parameter))
                            {
                                parameter = EscapeLikeText(parameter, '~');
                                escape = base.sql.Literal("~", sourceExpression);
                            }
                            SqlExpression pattern = base.sql.Parameter(parameter + "%", sourceExpression);
                            return base.sql.Like(expression, pattern, escape, sourceExpression);
                        }
                        throw new Exception("The used overload of System.String." + method.Name +
                                            " has no supported translation to ORACLE");

                    case "EndsWith":
                        if (1 == parameters.Count)
                        {
                            string parameter = base.Eval(parameters[0]).ToString();
                            SqlExpression escape = null;
                            if (RequiresEscape(parameter))
                            {
                                parameter = EscapeLikeText(parameter, '~');
                                escape = base.sql.Literal("~", sourceExpression);
                            }
                            SqlExpression pattern = base.sql.Parameter("%" + parameter, sourceExpression);
                            return base.sql.Like(expression, pattern, escape, sourceExpression);
                        }
                        throw new Exception("The used overload of System.String." + method.Name +
                                            " has no supported translation to ORACLE");

                    case "IndexOf":
                        if (1 == parameters.Count)
                        {
                            return
                                base.sql.Subtract(
                                    base.sql.FunctionCall(typeof (int), "INSTR",
                                                          new SqlExpression[] {expression, parameters[0]},
                                                          sourceExpression), 1);
                        }
                        if (2 == parameters.Count)
                        {
                            return
                                base.sql.Subtract(
                                    base.sql.FunctionCall(typeof (int), "INSTR",
                                                          new SqlExpression[]
                                                              {
                                                                  expression, parameters[0], base.sql.Add(parameters[1], 1)
                                                              }, sourceExpression), 1);
                        }
                        throw new Exception("The used overload of System.String." + method.Name +
                                            " has no supported translation to ORACLE");

                    case "LastIndexOf":
                        throw new Exception("The used overload of System.String." + method.Name +
                                            " has no supported translation to ORACLE");

                    case "Insert":
                        throw new Exception("The used overload of System.String." + method.Name +
                                            " has no supported translation to ORACLE");

                    case "PadLeft":
                        if (1 == parameters.Count)
                        {
                            return base.sql.FunctionCall(typeof (string), "LPAD",
                                                         new SqlExpression[] {expression, parameters[0]},
                                                         sourceExpression);
                        }
                        if (2 == parameters.Count)
                        {
                            return base.sql.FunctionCall(typeof (string), "LPAD",
                                                         new SqlExpression[] {expression, parameters[0], parameters[1]},
                                                         sourceExpression);
                        }
                        throw new Exception("The used overload of System.String." + method.Name +
                                            " has no supported translation to ORACLE");

                    case "PadRight":
                        if (1 == parameters.Count)
                        {
                            return base.sql.FunctionCall(typeof (string), "RPAD",
                                                         new SqlExpression[] {expression, parameters[0]},
                                                         sourceExpression);
                        }
                        if (2 == parameters.Count)
                        {
                            return base.sql.FunctionCall(typeof (string), "RPAD",
                                                         new SqlExpression[] {expression, parameters[0], parameters[1]},
                                                         sourceExpression);
                        }
                        throw new Exception("The used overload of System.String." + method.Name +
                                            " has no supported translation to ORACLE");

                    case "Remove":
                        throw new Exception("The used overload of System.String." + method.Name +
                                            " has no supported translation to ORACLE");

                    case "Replace":
                        return base.sql.FunctionCall(typeof (string), "REPLACE",
                                                     new SqlExpression[] {expression, parameters[0], parameters[1]},
                                                     sourceExpression);

                    case "Substring":
                        if (1 == parameters.Count)
                        {
                            return base.sql.FunctionCall(typeof (string), "SUBSTR",
                                                         new SqlExpression[]
                                                             {expression, base.sql.Add(parameters[0], 1)},
                                                         sourceExpression);
                        }
                        if (2 == parameters.Count)
                        {
                            return base.sql.FunctionCall(typeof (string), "SUBSTR",
                                                         new SqlExpression[]
                                                             {expression, base.sql.Add(parameters[0], 1), parameters[1]},
                                                         sourceExpression);
                        }
                        throw new Exception("The used overload of System.String." + method.Name +
                                            " has no supported translation to ORACLE");

                    case "Trim":
                        if (0 == parameters.Count)
                        {
                            return base.sql.FunctionCall(typeof (string), "TRIM", new SqlExpression[] {expression},
                                                         sourceExpression);
                        }
                        throw new Exception("The used overload of System.String." + method.Name +
                                            " has no supported translation to ORACLE");

                    case "ToLower":
                        return base.sql.FunctionCall(typeof (string), "LOWER", new SqlExpression[] {expression},
                                                     sourceExpression);

                    case "ToUpper":
                        return base.sql.FunctionCall(typeof (string), "UPPER", new SqlExpression[] {expression},
                                                     sourceExpression);

                    case "get_Chars":
                        throw new Exception("The used overload of System.String." + method.Name +
                                            " has no supported translation to ORACLE");
                    case "CompareTo":
                        throw new Exception("The used overload of System.String." + method.Name +
                                            " has no supported translation to ORACLE");
                }
            }
            throw new Exception("System.String method " + method.Name + " has no supported translation to ORACLE");
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="methodCall"></param>
        /// <param name="sourceExpression"></param>
        /// <returns></returns>
        private SqlExpression TranslateStringStaticMethod(SqlMethodCall methodCall, Expression sourceExpression)
        {
            SqlExpression expression = null;
            if ("Concat" == methodCall.Method.Name)
            {
                SqlClientArray array = methodCall.Arguments[0] as SqlClientArray;
                List<SqlExpression> expressions = null;
                expressions = null != array ? array.Expressions : methodCall.Arguments;
                if (0 == expressions.Count)
                {
                    return base.sql.Literal("", sourceExpression);
                }
                SqlExpression convert = this.ConvertToString(expressions[0]);
                for (int index = 1, max = expressions.Count; index < max; index++)
                {
                    convert = base.sql.Concat(new SqlExpression[] {convert, this.ConvertToString(expressions[index])});
                }
                return convert;
            }
            if (("Equals" == methodCall.Method.Name) && (2 == methodCall.Arguments.Count))
            {
                return base.sql.Binary(SqlNodeType.EQ, methodCall.Arguments[0], methodCall.Arguments[1]);
            }
            if (("Compare" == methodCall.Method.Name) && (2 == methodCall.Arguments.Count))
            {
                expression = base.CreateComparison(methodCall.Arguments[0], methodCall.Arguments[1], sourceExpression);
            }
            return expression;
        }

        //private SqlExpression TranslateTimeSpanInstanceMethod(SqlMethodCall mc, Expression source);
        //private SqlExpression TranslateTimeSpanStaticMethod(SqlMethodCall mc, Expression source);
        //private SqlExpression VisitNewDateTime(SqlNew sox, Expression source);
        //private SqlExpression VisitNewTimeSpan(SqlNew sox);

        #endregion
    }
}